package com.code.AZPlan.service.impl;

import com.alibaba.fastjson.JSON;
import com.code.AZPlan.api.AZPlanStaticIncomeService;
import com.code.AZPlan.model.bo.*;
import com.code.AZPlan.model.po.*;
import com.code.AZPlan.service.mapper.AZPlanStaticIncomeMapper;
import com.code.commons.constants.ServerConstants;
import com.code.commons.enums.CallSourceEnum;
import com.code.commons.service.impl.BaseMySqlCrudServiceImpl;
import com.code.commons.util.*;
import com.code.user.model.bo.UserCoinBalanceBO;
import com.code.user.model.bo.UserRecommenderBO;
import com.code.user.model.po.User;
import com.code.user.model.po.UserCoinBalance;
import com.code.user.model.po.UserRecommender;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import xwc.usertree.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AZ计划静态收益服务接口实现（AZ基础服务层）
 *
 * @author tangyifei
 * @since 2019年12月20日17:49:31
 */
@Service
@Slf4j
public class AZPlanStaticIncomeServiceImpl extends BaseMySqlCrudServiceImpl<AZPlanBalance, String> implements AZPlanStaticIncomeService {

    @Resource
    TimeUtil timeUtil;

    @Resource
    private AZPlanStaticIncomeMapper azPlanStaticIncomeMapper;

    /**
     * 资金划转
     *
     * @param moneyTransferRecord 资金划转对象
     * @return 资金划转对象
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public MoneyTransferRecord moneyTransfer(MoneyTransferRecord moneyTransferRecord) {
        Assert.notNull(moneyTransferRecord, "moneyTransferRecord is not null");
        moneyTransferRecord.setUpdateTime(new Date());
        int count = azPlanStaticIncomeMapper.updateMoneyTransferRecord(moneyTransferRecord);
        int transferType = moneyTransferRecord.getTransferType();
        String userId = moneyTransferRecord.getUserId();
        String coinType = moneyTransferRecord.getCoinType();
        String coinAddress = moneyTransferRecord.getCoinAddress();
        if (count > 0) {
            BigDecimal amount = moneyTransferRecord.getAmount();
            //加入钱包转出流水
            TransferInOutFlow transferInOutFlow = TransferInOutFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(userId)
                    .coinType(coinType).address(coinAddress).fee(BigDecimal.ZERO)
                    .amount(amount).status(ServerConstants.CONFIRMED).build();
            transferInOutFlow.setCreateTime(new Date());
            transferInOutFlow.setUpdateTime(new Date());
            int userCoinBalanceCount = 0;
            int azBalanceCount = 0;
            if (transferType == 1) {
                UserCoinBalance userCoinBalance = UserCoinBalance.builder().userId(userId).coinType(coinType)
                        .coinAddress(coinAddress).coinBalance(amount)
                        .version(moneyTransferRecord.getXwcCoinBalanceVersion()).build();
                userCoinBalance.setUpdateTime(new Date());
                //从钱包划转到AZ计划
                if (null != userCoinBalance.getVersion()) {
                    userCoinBalanceCount = azPlanStaticIncomeMapper.reduceUserCoinBalance(userCoinBalance);
                }
                //增加AZ钱包的金额
                AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(userId).coinType(coinType).version(moneyTransferRecord.getXwcAZPlanWalletVersion())
                        .amount(amount).build();
                azPlanWallet.setUpdateTime(new Date());
                if (null != azPlanWallet.getVersion() && userCoinBalanceCount > 0) {
                    azBalanceCount = azPlanStaticIncomeMapper.addAZPlanWalletAmount(azPlanWallet);
                }
                transferInOutFlow.setFlowType(ServerConstants.WITHDRAW);
            }
            if (transferType == 2) {
                UserCoinBalance userCoinBalance = UserCoinBalance.builder().userId(userId).coinType(coinType)
                        .coinAddress(coinAddress).coinBalance(amount).build();
                if (ServerConstants.XWC.equals(coinType)) {
                    userCoinBalance.setVersion(moneyTransferRecord.getXwcCoinBalanceVersion());
                }
                if (ServerConstants.BTC.equals(coinType)) {
                    userCoinBalance.setVersion(moneyTransferRecord.getBtcCoinBalanceVersion());
                }
                userCoinBalance.setUpdateTime(new Date());
                //从AZ计划划转到钱包
                if (null != userCoinBalance.getVersion()) {
                    userCoinBalanceCount = azPlanStaticIncomeMapper.addUserCoinBalance(userCoinBalance);
                }
                AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(userId).coinType(coinType)
                        .amount(amount).build();
                if (ServerConstants.XWC.equals(coinType)) {
                    azPlanWallet.setVersion(moneyTransferRecord.getXwcAZPlanWalletVersion());
                }
                if (ServerConstants.BTC.equals(coinType)) {
                    azPlanWallet.setVersion(moneyTransferRecord.getBtcAZPlanWalletVersion());
                }
                azPlanWallet.setUpdateTime(new Date());
                if (null != azPlanWallet.getVersion() && userCoinBalanceCount > 0) {
                    azBalanceCount = azPlanStaticIncomeMapper.reduceAZPlanWalletAmount(azPlanWallet);
                }
                transferInOutFlow.setFlowType(ServerConstants.RECHARGE);
            }
            if (azBalanceCount > 0 && userCoinBalanceCount > 0) {
                azPlanStaticIncomeMapper.insertTransferInOutFlow(transferInOutFlow);
            }
        }
        return getMoneyTransferByUserIdAndCoinType(userId, coinType, null, transferType);
    }

    /**
     * 入金
     *
     * @param entryMoneyBO 入金业务对象
     * @return 入金业务对象
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public EntryMoneyBO entryMoney(EntryMoneyBO entryMoneyBO) throws Exception {
        Assert.notNull(entryMoneyBO, "entryMoneyBO is not null");
        String userId = entryMoneyBO.getUserId();
        //获取内存树
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        TreeTransaction treeTx = tree.createTransaction();
        Integer entryMoneyChannelType = entryMoneyBO.getEntryMoneyChannelType();
        if (null != tree) {
            String recommenderId;
            Integer azPlanStatus;
            Integer usdtversion;
            Integer orePoolVersion;
            UserCoinBalanceBO userCoinBalanceBO;
            TransferInOutFlow transferInOutFlow;
            IncomeFlow incomeFlow;
            Pair<List<Account>, List<AccountTree.AccountIncome>> result = null;
            int count;
            BigDecimal orePool;
            //1、根据被推荐人的主键查询被推荐的记录
            List<UserRecommender> userRecommenders = azPlanStaticIncomeMapper.getUserRecommenderByRecommenderUserId(userId);
            try {
                Account entryUserAccount = tree.findInTreeByKey(userId);
                if (CollectionUtil.isNotEmpty(userRecommenders)) {
                    //某个用户入金之前要更新他的推荐人的推荐收益
                    List<UserRelativeVersionBO> recommenderRelativeVersionBOList = entryMoneyBO.getRecommenderRelativeVersionBOList();
//                List<AZPlanBalance> userOrePoolVersionList = entryMoneyBO.getUserOrePoolVersionList();
                    //这里只会循环一次，不影响性能
                    for (UserRecommender userRecommender : userRecommenders) {
                        recommenderId = userRecommender.getRecommenderId();
                        azPlanStatus = userRecommender.getAzPlanStatus();
                        if (ServerConstants.NO_CONFIRM == azPlanStatus) {
                            //被推荐人是第一次入金
                            // find account in tree. if not exsits, addnewaccount, otherwise activate
                            if (null == entryUserAccount) {
                                Pair<List<Account>, List<AccountTree.AccountIncome>> result1 = tree.addNewAccount(treeTx, userId, entryMoneyBO.getPhone(), recommenderId, new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                                Pair<List<Account>, List<AccountTree.AccountIncome>> result2 = tree.activateNewAccount(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                                List<Account> accountUnionList1 = Lists.newArrayList();
                                List<Account> accountUnionList2 = Lists.newArrayList();
                                List<Account> accountUnionList3 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions1 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions2 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions3 = Lists.newArrayList();
                                if (null != result1) {
                                    accountUnionList1 = result1.getLeft();
                                    incomeUnions1 = result1.getRight();
                                }
                                if (null != result2) {
                                    accountUnionList2 = result2.getLeft();
                                    incomeUnions2 = result2.getRight();
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList1)) {
                                    accountUnionList3.addAll(accountUnionList1);
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList2)) {
                                    accountUnionList3.addAll(accountUnionList2);
                                }
                                if (CollectionUtil.isNotEmpty(incomeUnions1)) {
                                    incomeUnions3.addAll(incomeUnions1);
                                }
                                if (CollectionUtil.isNotEmpty(incomeUnions2)) {
                                    incomeUnions3.addAll(incomeUnions2);
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList3) || CollectionUtil.isNotEmpty(incomeUnions3)) {
                                    result = Pair.of(accountUnionList3, incomeUnions3);
                                }
                            } else {
                                Pair<List<Account>, List<AccountTree.AccountIncome>> result2 = tree.activateNewAccount(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                                Pair<List<Account>, List<AccountTree.AccountIncome>> result1 = tree.addNewDeposit(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT), false);
                                List<Account> accountUnionList1 = Lists.newArrayList();
                                List<Account> accountUnionList2 = Lists.newArrayList();
                                List<Account> accountUnionList3 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions1 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions2 = Lists.newArrayList();
                                List<AccountTree.AccountIncome> incomeUnions3 = Lists.newArrayList();
                                if (null != result1) {
                                    accountUnionList1 = result1.getLeft();
                                    incomeUnions1 = result1.getRight();
                                }
                                if (null != result2) {
                                    accountUnionList2 = result2.getLeft();
                                    incomeUnions2 = result2.getRight();
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList1)) {
                                    accountUnionList3.addAll(accountUnionList1);
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList2)) {
                                    accountUnionList3.addAll(accountUnionList2);
                                }
                                if (CollectionUtil.isNotEmpty(incomeUnions1)) {
                                    incomeUnions3.addAll(incomeUnions1);
                                }
                                if (CollectionUtil.isNotEmpty(incomeUnions2)) {
                                    incomeUnions3.addAll(incomeUnions2);
                                }
                                if (CollectionUtil.isNotEmpty(accountUnionList3) || CollectionUtil.isNotEmpty(incomeUnions3)) {
                                    result = Pair.of(accountUnionList3, incomeUnions3);
                                }
                            }
                        } else {
                            //被推荐人不是第一次入金
                            if (entryMoneyChannelType == 1) {
                                result = tree.addNewDeposit(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT), true);
                            }
                            if (entryMoneyChannelType == 2) {
                                result = tree.addNewDeposit(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.TWO_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.TWO_CHANNEL_USD_AMOUNT), true);
                            }
                            if (entryMoneyChannelType == 3) {
                                result = tree.addNewDeposit(treeTx, userId, userRecommender.getOrePool(), new BigDecimal(ServerConstants.THREE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.THREE_CHANNEL_USD_AMOUNT), true);
                            }
                        }
                        if (null != result) {
                            //更新用户级别和渠道
                            updateUserVipLevelAndChannel(userId, result);
                            //更新收益
                            List<AccountTree.AccountIncome> incomes = result.getRight();
                            log.info("update user income. userId:{}, incomes info :{}", userId, incomes.toString());
                            if (CollectionUtil.isNotEmpty(incomes)) {
                                Income income;
                                Account account;
                                Account parentAccount;
                                //直推收益（其实直推收益也属于静态收益的一部分）
                                BigDecimal directIncome;
                                //静态收益
                                BigDecimal staticIncome;
                                //动态收益
                                BigDecimal dynamicIncome;
                                //平级代数收益（一级20%, 二级10%）
                                BigDecimal generationIncome;
                                String parentKey;
                                for (AccountTree.AccountIncome accountIncome : incomes) {
                                    income = accountIncome.getIncome();
                                    account = accountIncome.getAccount();
                                    parentAccount = account.getParent();
                                    //待定
                                    parentKey = account.getKey();
                                    if (StringUtil.isEmpty(parentKey)) {
                                        parentKey = parentAccount.getKey();
                                    }
                                    directIncome = income.getDirectIncome();
                                    if (null == directIncome) {
                                        directIncome = BigDecimal.ZERO;
                                    }
                                    dynamicIncome = income.getDynamicIncome();
                                    if (null == dynamicIncome) {
                                        dynamicIncome = BigDecimal.ZERO;
                                    }
                                    generationIncome = income.getGenerationIncome();
                                    if (null == generationIncome) {
                                        generationIncome = BigDecimal.ZERO;
                                    }
                                    staticIncome = income.getStaticIncome();
                                    if (null == staticIncome) {
                                        staticIncome = BigDecimal.ZERO;
                                    }
                                    //更新有上级推荐人的账户
                                    UserRelativeVersionBO userRelativeVersionBO = azPlanStaticIncomeMapper.getUserRelativeVersionBO(parentKey);
                                    if (null != userRelativeVersionBO) {
                                        orePool = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(parentKey).getOrePool();
                                        //如果推荐人的矿池释放完了就不能得到相关的收益
                                        if (orePool.compareTo(BigDecimal.ZERO) > 0) {
                                            usdtversion = userRelativeVersionBO.getUsdtCoinBalanceVersion();
                                            orePoolVersion = userRelativeVersionBO.getOrePoolVersion();
                                            userCoinBalanceBO = azPlanStaticIncomeMapper.getUserCoinBalanceBO(parentKey);
                                            //由于客户端传递过来的版本号的过程中，可能推荐人的版本号已经发生变化，为了避免这种情况发生，最好让客户端重新进入AZ资产页面并重新入金
                                            if (usdtversion.intValue() != userCoinBalanceBO.getUsdtCoinBalanceVersion().intValue() || orePoolVersion.intValue() != userCoinBalanceBO.getOrePoolVersion().intValue()) {
                                                log.error("version inconsistent, 客户端传递过来的版本号:[{}, {}], 实际版本号:[{},{}]", usdtversion, orePoolVersion, userCoinBalanceBO.getUsdtCoinBalanceVersion(), userCoinBalanceBO.getOrePoolVersion());
                                                //todo 数据需要回滚包括db里的数据和内存树中的数据
                                                treeTx.rollback();
                                                entryMoneyBO.setEntryMoneyFailureFlag(1);
                                                return entryMoneyBO;
                                            }
                                            //增加推荐人的USDT钱包余额（增加直推收益）
                                            if (directIncome.compareTo(BigDecimal.ZERO) != 0) {
                                                azPlanStaticIncomeMapper.addUSDTCoinBalance(parentKey, directIncome, ServerConstants.USDT, usdtversion, new Date());
                                                //更新推荐人的矿池大小
                                                count = azPlanStaticIncomeMapper.reduceAZPlanBalance(parentKey, directIncome, orePoolVersion, new Date());
                                                if (count > 0) {
                                                    //检查矿池大小是否为0
                                                    orePool = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(parentKey).getOrePool();
                                                    //筛选出释放完的USD，并更新状态为已经释放完，即状态改为2
                                                    if (orePool.compareTo(BigDecimal.ZERO) <= 0) {
                                                        azPlanStaticIncomeMapper.updateAZPlanStatus(parentKey, ServerConstants.RELEANSED);
                                                    }
                                                }
                                                //增加推荐人的usdt的转入流水
                                                transferInOutFlow = TransferInOutFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(parentKey)
                                                        .coinType(ServerConstants.USDT).flowType(1).fee(BigDecimal.ZERO)
                                                        .amount(directIncome).status(ServerConstants.CONFIRMED).build();
                                                transferInOutFlow.setCreateTime(new Date());
                                                transferInOutFlow.setUpdateTime(new Date());
                                                azPlanStaticIncomeMapper.insertTransferInOutFlow(transferInOutFlow);
                                                //增加推荐人的收益流水（直推收益）
                                                incomeFlow = IncomeFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(parentKey).incomeType(2).incomeAmount(directIncome)
                                                        .build();
                                                incomeFlow.setCreateTime(new Date());
                                                incomeFlow.setUpdateTime(new Date());
                                                azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
                                                //更新本次入金用户的推荐状态为推荐成功,并且添加推荐人奖励记录
                                                azPlanStaticIncomeMapper.updateUserRecommender(parentKey, userId, directIncome, null, new Date());
                                                //添加推荐人奖励记录
                                                String recommenderRewards = StringUtils.join(account.getCurrentRecommenderRewardArr(), ",");
                                                if (account.getParentKey() != null) {
                                                    azPlanStaticIncomeMapper.updateUserRecommender(account.getParentKey(),
                                                            account.getKey(), null, recommenderRewards, new Date());
                                                }
                                            }
                                            //用户入金，不会产生静态收益和团队奖励
//                                            dynamicIncome = BigDecimalUtils.add(dynamicIncome, String.valueOf(generationIncome));
//                                            if (dynamicIncome.compareTo(BigDecimal.ZERO) != 0) {
//                                                azPlanStaticIncomeMapper.updateAZPlanDynamicTotalIncome(parentKey, dynamicIncome, orePoolVersion);
//                                                //版本号+1
//                                                orePoolVersion++;
//                                                //增加推荐人的收益流水（团队收益）
//                                                incomeFlow = IncomeFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(parentKey).incomeType(3).incomeAmount(dynamicIncome)
//                                                        .build();
//                                                incomeFlow.setCreateTime(new Date());
//                                                incomeFlow.setUpdateTime(new Date());
//                                                azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
//                                            }
                                            //增加静态收益
//                                            if (staticIncome.compareTo(BigDecimal.ZERO) != 0) {
//                                                count = azPlanStaticIncomeMapper.updateAZPlanStaticTotalIncome(parentKey, staticIncome, orePoolVersion);
//                                                if (count > 0) {
//                                                    //检查矿池大小是否为0
//                                                    orePool = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(parentKey).getOrePool();
//                                                    //筛选出释放完的USD，并更新状态为已经释放完，即状态改为2
//                                                    if (orePool.compareTo(BigDecimal.ZERO) <= 0) {
//                                                        azPlanStaticIncomeMapper.updateAZPlanStatus(parentKey, ServerConstants.RELEANSED);
//                                                    }
//                                                }
//                                                incomeFlow = new IncomeFlow();
//                                                incomeFlow.setId(SnowFlakeIdUtil.defaultId());
//                                                incomeFlow.setIncomeAmount(staticIncome);
//                                                incomeFlow.setUserId(parentKey);
//                                                incomeFlow.setIncomeType(1);
//                                                incomeFlow.setCreateTime(new Date());
//                                                incomeFlow.setUpdateTime(new Date());
//                                                azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
//                                            }
                                        } else {
                                            log.info("userId:{}, parentKey:{} orePool already release", userId, parentKey);
                                            //更新本次入金用户的推荐状态为推荐成功,并且添加推荐人奖励记录
                                            if (entryUserAccount != null && entryUserAccount.getParentKey() != null) {
                                                azPlanStaticIncomeMapper.updateUserRecommender(parentKey, userId, directIncome, null, new Date());
                                            }
                                        }
                                    }
                                }
                            } else {
                                //上级没有直推奖励的情况下，不影响本次用户的有效入金,更新本次入金用户的推荐状态为推荐成功
                                if (entryUserAccount != null && entryUserAccount.getParentKey() != null) {
                                    log.info("userId:{}, incomes info is empty, update user status", userId);
                                    azPlanStaticIncomeMapper.updateUserRecommender(entryUserAccount.getParentKey(), userId, null, null, new Date());
                                }
                            }
                        }
                    }
                } else {
                    //判断用户是否是第一次入金
                    AZPlanBalance azPlanBalance = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(userId);
                    azPlanStatus = azPlanBalance.getAzPlanStatus();
                    if (ServerConstants.NO_CONFIRM == azPlanStatus) {
                        if (null == entryUserAccount) {
                            tree.addNewAccount(treeTx, userId, entryMoneyBO.getPhone(), null, new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                        }
                        tree.activateNewAccount(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                    } else {
                        if (entryMoneyChannelType == 1) {
                            tree.activateNewAccount(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT));
                            result = tree.addNewDeposit(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT), false);
                        }
                        if (entryMoneyChannelType == 2) {
                            tree.activateNewAccount(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.TWO_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.TWO_CHANNEL_USD_AMOUNT));
                            result = tree.addNewDeposit(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.TWO_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.TWO_CHANNEL_USD_AMOUNT), false);
                        }
                        if (entryMoneyChannelType == 3) {
                            tree.activateNewAccount(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.THREE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.THREE_CHANNEL_USD_AMOUNT));
                            result = tree.addNewDeposit(treeTx, userId, azPlanBalance.getOrePool(), new BigDecimal(ServerConstants.THREE_CHANNEL_USDT_LOWEST_AMOUNT), new BigDecimal(ServerConstants.THREE_CHANNEL_USD_AMOUNT), false);
                        }
                        //更新用户级别和渠道
                        updateUserVipLevelAndChannel(userId, result);
                    }
                }
                treeTx.commit();
            } catch (Exception e) {
                //异常情况下，数据需要回滚
                treeTx.rollback();
                log.error("更新内存树时发生异常：【{}】", e);
                throw e;
            }
        }

        String entryOtherCoinType = entryMoneyBO.getEntryOtherCoinType();
        BigDecimal xwcExchangeUSDAmount = entryMoneyBO.getXwcExchangeUSDAmount();
        int moneyTransferVersionVersion = entryMoneyBO.getMoneyTransferVersion();
        if (entryMoneyChannelType == 1) {
            //第一步：增加AZ计划用户账户信息(即矿池信息)，修改矿池信息之前要判断该矿池之前是否入过金，如果状态是已释放完，则之前入过金，累计入金不需要计算，直接汇总入金流水
            AZPlanBalance aZPlanBalance = AZPlanBalance.builder().userId(userId).orePool(new BigDecimal(ServerConstants.ONE_CHANNEL_USD_AMOUNT))
                    .azPlanStatus(1).investTime(new Date()).build();
            AZPlanBalance azPlanBalanceForDB = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(userId);
            //如果状态已经为已释放完，那么再次入金就算是复投了
            if (2 == azPlanBalanceForDB.getAzPlanStatus()) {
                aZPlanBalance.setDoubleThrowState(1);
            }
            aZPlanBalance.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.updateAZPlanBalanceByUserId(aZPlanBalance);
            //第二步：扣除资金划转记录列表中的某一个用户的相关币种从钱包划转到AZ计划的金额
            MoneyTransferRecord moneyTransferRecord = MoneyTransferRecord.builder().coinType(entryOtherCoinType).amount(xwcExchangeUSDAmount)
                    .userId(userId).transferType(1).version(moneyTransferVersionVersion).build();
            moneyTransferRecord.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceMoneyTransferAmount(moneyTransferRecord);
            //第三步：扣除AZ计划钱包的余额
            AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(userId).coinType(entryOtherCoinType).version(entryMoneyBO.getAzPlanWalletVersion())
                    .amount(xwcExchangeUSDAmount).build();
            azPlanWallet.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceAZPlanWalletAmount(azPlanWallet);
            //第四步：插入AZ入金流水
            EntryMoneyFlow entryMoneyFlow = EntryMoneyFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(userId).coinTypeBefore(entryOtherCoinType)
                    .coinTypeAfter(ServerConstants.USDT).entryMoneyChannel(ServerConstants.ONE_CHANNEL).amount(new BigDecimal(ServerConstants.ONE_CHANNEL_USDT_LOWEST_AMOUNT))
                    .status(ServerConstants.CONFIRMED).entryAmount(xwcExchangeUSDAmount).build();
            entryMoneyFlow.setCreateTime(new Date());
            entryMoneyFlow.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.insertEntryMoneyFlow(entryMoneyFlow);
        }

        if (entryMoneyChannelType == 2) {
            //第一步：增加AZ计划用户账户信息(即矿池信息)，修改矿池信息之前要判断该矿池之前是否入过金，如果状态是已释放完，则之前入过金，累计入金不需要计算，直接汇总入金流水
            AZPlanBalance aZPlanBalance = AZPlanBalance.builder().userId(userId).orePool(new BigDecimal(ServerConstants.TWO_CHANNEL_USD_AMOUNT))
                    .azPlanStatus(1).investTime(new Date()).build();
            AZPlanBalance azPlanBalanceForDB = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(userId);
            //如果状态已经为已释放完，那么再次入金就算是复投了
            if (2 == azPlanBalanceForDB.getAzPlanStatus()) {
                aZPlanBalance.setDoubleThrowState(1);
            }
            aZPlanBalance.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.updateAZPlanBalanceByUserId(aZPlanBalance);
            //第二步：扣除资金划转记录列表中的某一个用户的相关币种从钱包划转到AZ计划的金额
            MoneyTransferRecord moneyTransferRecord = MoneyTransferRecord.builder().coinType(entryOtherCoinType).amount(xwcExchangeUSDAmount)
                    .userId(userId).transferType(1).version(moneyTransferVersionVersion).build();
            moneyTransferRecord.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceMoneyTransferAmount(moneyTransferRecord);
            //第三步：扣除AZ计划钱包的余额
            AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(userId).coinType(entryOtherCoinType).version(entryMoneyBO.getAzPlanWalletVersion())
                    .amount(xwcExchangeUSDAmount).build();
            azPlanWallet.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceAZPlanWalletAmount(azPlanWallet);
            //第四步：插入AZ入金流水
            EntryMoneyFlow entryMoneyFlow = EntryMoneyFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(userId).coinTypeBefore(entryOtherCoinType)
                    .coinTypeAfter(ServerConstants.USDT).entryMoneyChannel(ServerConstants.TWO_CHANNEL).amount(new BigDecimal(ServerConstants.TWO_CHANNEL_USDT_LOWEST_AMOUNT))
                    .status(ServerConstants.CONFIRMED).entryAmount(xwcExchangeUSDAmount).build();
            entryMoneyFlow.setCreateTime(new Date());
            entryMoneyFlow.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.insertEntryMoneyFlow(entryMoneyFlow);
        }

        if (entryMoneyChannelType == 3) {
            //第一步：增加AZ计划用户账户信息(即矿池信息)，修改矿池信息之前要判断该矿池之前是否入过金，如果状态是已释放完，则之前入过金，累计入金不需要计算，直接汇总入金流水
            AZPlanBalance aZPlanBalance = AZPlanBalance.builder().userId(userId).orePool(new BigDecimal(ServerConstants.THREE_CHANNEL_USD_AMOUNT))
                    .azPlanStatus(1).investTime(new Date()).build();
            AZPlanBalance azPlanBalanceForDB = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(userId);
            //如果状态已经为已释放完，那么再次入金就算是复投了
            if (2 == azPlanBalanceForDB.getAzPlanStatus()) {
                aZPlanBalance.setDoubleThrowState(1);
            }
            aZPlanBalance.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.updateAZPlanBalanceByUserId(aZPlanBalance);
            //第二步：扣除资金划转记录列表中的某一个用户的相关币种从钱包划转到AZ计划的金额
            MoneyTransferRecord moneyTransferRecord = MoneyTransferRecord.builder().coinType(entryOtherCoinType).amount(xwcExchangeUSDAmount)
                    .userId(userId).transferType(1).version(moneyTransferVersionVersion).build();
            moneyTransferRecord.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceMoneyTransferAmount(moneyTransferRecord);
            //第三步：扣除AZ计划钱包的余额
            AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(userId).coinType(entryOtherCoinType).version(entryMoneyBO.getAzPlanWalletVersion())
                    .amount(xwcExchangeUSDAmount).build();
            azPlanWallet.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.reduceAZPlanWalletAmount(azPlanWallet);
            //第四步：插入AZ入金流水
            EntryMoneyFlow entryMoneyFlow = EntryMoneyFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(userId).coinTypeBefore(entryOtherCoinType)
                    .coinTypeAfter(ServerConstants.USDT).entryMoneyChannel(ServerConstants.THREE_CHANNEL).amount(new BigDecimal(ServerConstants.THREE_CHANNEL_USDT_LOWEST_AMOUNT))
                    .status(ServerConstants.CONFIRMED).entryAmount(xwcExchangeUSDAmount).build();
            entryMoneyFlow.setCreateTime(new Date());
            entryMoneyFlow.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.insertEntryMoneyFlow(entryMoneyFlow);
        }

        return entryMoneyBO;
    }

    /**
     * 更新用户级别和渠道
     *
     * @param userId
     * @param result
     */
    private void updateUserVipLevelAndChannel(String userId, Pair<List<Account>, List<AccountTree.AccountIncome>> result) {
        List<Account> accountList = result.getLeft();
        //账户去重
        HashSet<Account> hashSets = Sets.newHashSet(accountList);
        //List<UserVipBO> userVipBOS = getUserVipBOList(hashSets, null);
        List<UserVipBO> userVipBOS = Lists.newArrayListWithExpectedSize(hashSets.size());
        UserVipBO userVipBO;
        Integer level;
        if (CollectionUtil.isNotEmpty(hashSets)) {
            for (Account accountHashSet : hashSets) {
                log.info("update user vip lever and channel. userId:{}, account info :{}", userId, accountHashSet.toString());
                //达到3000的渠道的条件： 邀请6个有效用户
                if (AccountLevels.TWO_CHANNEL == AccountLevels.getAccountChannel(accountHashSet)) {
                    userVipBO = new UserVipBO();
                    userVipBO.setRecommendId(accountHashSet.getKey());
                    //开通了3000渠道的情况下，有可能为V0（用户未复投） 也有可能为V1(用户已复投)
                    String vipLevel = accountHashSet.getLevel().intValue() == AccountLevels.V1 ? ServerConstants.ONE_LEVEL : ServerConstants.ZERO_LEVEL;
                    userVipBO.setUserVipLevel(vipLevel);
                    userVipBO.setChannel(ServerConstants.TWO_CHANNEL_USDT_LOWEST_AMOUNT);
                    userVipBOS.add(userVipBO);
                }
                if (AccountLevels.V2 == accountHashSet.getLevel()) {
                    userVipBO = new UserVipBO();
                    userVipBO.setRecommendId(accountHashSet.getKey());
                    userVipBO.setUserVipLevel(ServerConstants.TWO_LEVEL);
                    userVipBO.setChannel(ServerConstants.THREE_CHANNEL_USDT_LOWEST_AMOUNT);
                    userVipBOS.add(userVipBO);
                }
                if (AccountLevels.V3 == accountHashSet.getLevel()) {
                    userVipBO = new UserVipBO();
                    userVipBO.setRecommendId(accountHashSet.getKey());
                    userVipBO.setUserVipLevel(ServerConstants.THREE_LEVEL);
                    userVipBOS.add(userVipBO);
                }
            }
            //批量更新VIP级别
            if (CollectionUtil.isNotEmpty(userVipBOS)) {
                azPlanStaticIncomeMapper.patchUpdateAZPlanBalanceUserVipLevelList(userVipBOS);
                azPlanStaticIncomeMapper.patchUpdateUserLevel(userVipBOS);
                //批量更新用户的解锁通道
                List<UserVipBO> userVips = Lists.newArrayListWithExpectedSize(userVipBOS.size());
                for (UserVipBO userVip : userVipBOS) {
                    if (StringUtil.isNotEmpty(userVip.getChannel())) {
                        userVips.add(userVip);
                    }
                }
                if (CollectionUtil.isNotEmpty(userVips)) {
                    azPlanStaticIncomeMapper.patchUpdateUserEntryMoneyChannel(userVips);
                }
            }
        }
    }

    /**
     * 通过用户主键获取矿池信息
     *
     * @param userId 用户主键
     * @return 矿池信息
     */
    @Override
    public AZPlanBalance getAZPlanBalanceByUserId(String userId) {
        return azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(userId);
    }

    /**
     * 通过用户主键和币种类型获取资金划转信息
     *
     * @param userId       用户主键
     * @param coinType     币种类型
     * @param coinAddress  币种地址
     * @param transferType 1表示从钱包划转到AZ计划   2表示从AZ计划划转到钱包
     * @return 资金划转信息
     */
    @Override
    public MoneyTransferRecord getMoneyTransferByUserIdAndCoinType(String userId, String coinType, String coinAddress, Integer transferType) {
        MoneyTransferRecord moneyTransferRecord = MoneyTransferRecord.builder().build();
        if (StringUtil.isNotEmpty(coinType)) {
            moneyTransferRecord = azPlanStaticIncomeMapper.getMoneyTransferByUserIdAndCoinType(userId, coinType, transferType);
        }
        //获取用户余额
        if (StringUtil.isNotEmpty(coinAddress)) {
            BigDecimal coinBalance = azPlanStaticIncomeMapper.getUserCoinBalance(userId, coinType, coinAddress);
            moneyTransferRecord.setCoinBalance(coinBalance);
        }
        //通过币种类型和用户主键获取AZ计划的钱包版本号
        if (transferType == 1) {
            Integer xwcCoinBalanceVersion = azPlanStaticIncomeMapper.getUserCoinBalanceVersion(userId, ServerConstants.XWC);
            moneyTransferRecord.setXwcCoinBalanceVersion(xwcCoinBalanceVersion);
            AZPlanWallet azPlanWallet = azPlanStaticIncomeMapper.getAZPlanWallet(userId, coinType);
            if (null != azPlanWallet) {
                moneyTransferRecord.setXwcAZPlanWalletVersion(azPlanWallet.getVersion());
                moneyTransferRecord.setXwcAZPlanBalance(azPlanWallet.getAmount());
            }
        }
        if (transferType == 2) {
            AZPlanWallet xwcAZPlanWallet = azPlanStaticIncomeMapper.getAZPlanWallet(userId, ServerConstants.XWC);
            if (null != xwcAZPlanWallet) {
                moneyTransferRecord.setXwcAZPlanWalletVersion(xwcAZPlanWallet.getVersion());
                moneyTransferRecord.setXwcAZPlanBalance(xwcAZPlanWallet.getAmount());
            }
            AZPlanWallet btcAZPlanWallet = azPlanStaticIncomeMapper.getAZPlanWallet(userId, ServerConstants.BTC);
            if (null != btcAZPlanWallet) {
                moneyTransferRecord.setBtcAZPlanWalletVersion(btcAZPlanWallet.getVersion());
                moneyTransferRecord.setBtcAZPlanBalance(btcAZPlanWallet.getAmount());
            }
            Integer xwcCoinBalanceVersion = azPlanStaticIncomeMapper.getUserCoinBalanceVersion(userId, ServerConstants.XWC);
            moneyTransferRecord.setXwcCoinBalanceVersion(xwcCoinBalanceVersion);
            Integer btcCoinBalanceVersion = azPlanStaticIncomeMapper.getUserCoinBalanceVersion(userId, ServerConstants.BTC);
            moneyTransferRecord.setBtcCoinBalanceVersion(btcCoinBalanceVersion);
            Integer usdtCoinBalanceVersion = azPlanStaticIncomeMapper.getUserCoinBalanceVersion(userId, ServerConstants.USDT);
            moneyTransferRecord.setUsdtCoinBalanceVersion(usdtCoinBalanceVersion);
        }
        return moneyTransferRecord;
    }

    /**
     * 获取某一个用户的矿池大小
     *
     * @param userId   用户主键
     * @param coinType 币种地址
     * @return 矿池大小
     */
    @Override
    public Map<String, Object> getOrePool(String userId, String coinType) {
        Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(1 << 3);
        //获取矿池的大小
        BigDecimal orePool = azPlanStaticIncomeMapper.getOrePool(userId);
        resultMap.put("orePoolSize", setScale(orePool, null));
        //获取用户币种余额的版本号
        Integer version = azPlanStaticIncomeMapper.getUserCoinBalanceVersion(userId, coinType);
        resultMap.put("userCoinBalanceVersion", version);
        return resultMap;
    }

    /**
     * 获取用户总的收益（静态收益和动态收益）
     *
     * @param userId 用户主键
     * @return 总的收益
     */
    @Override
    public BigDecimal getTotalIncomeAmount(String userId) {
        BigDecimal totalStaticIncome = azPlanStaticIncomeMapper.getTotalStaticIncomeAmount(userId);
        if (null == totalStaticIncome) {
            totalStaticIncome = BigDecimal.ZERO;
        }
        BigDecimal totalDynamicIncome = azPlanStaticIncomeMapper.getTotalDynamicIncomeAmount(userId);
        if (null == totalDynamicIncome) {
            totalDynamicIncome = BigDecimal.ZERO;
        }
        BigDecimal totalIncomeAmount = BigDecimalUtils.add(String.valueOf(totalStaticIncome), String.valueOf(totalDynamicIncome));
        BigDecimal totalExchangeAmount = azPlanStaticIncomeMapper.getTotalExchangeAmount(userId);
        if (null == totalExchangeAmount) {
            totalExchangeAmount = BigDecimal.ZERO;
        }
        BigDecimal remainIncomeAmount = BigDecimalUtils.subtract(String.valueOf(totalIncomeAmount), String.valueOf(totalExchangeAmount));
        return remainIncomeAmount;
    }

    /**
     * 插入兑换流水并增加用户的AZ计划钱包的余额
     *
     * @param exchangeFlow 兑换流水实体
     * @return 兑换流水实体
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public ExchangeFlow insertExchangeFlow(ExchangeFlow exchangeFlow) {
        AZPlanWallet azPlanWallet = AZPlanWallet.builder().userId(exchangeFlow.getUserId()).coinType(exchangeFlow.getCoinTypeAfter())
                .version(exchangeFlow.getAzPlanWalletVersion())
                .amount(exchangeFlow.getExchangeAmount()).build();
        azPlanWallet.setUpdateTime(new Date());
        int count = azPlanStaticIncomeMapper.addAZPlanWalletAmount(azPlanWallet);
        if (count > 0) {
            exchangeFlow.setId(SnowFlakeIdUtil.defaultId());
            exchangeFlow.setCreateTime(new Date());
            exchangeFlow.setUpdateTime(new Date());
            azPlanStaticIncomeMapper.insertExchangeFlow(exchangeFlow);
        }
        return exchangeFlow;
    }

    /**
     * 根据用户主键和币种类型获取相关XWC币种的AZ计划钱包金额
     *
     * @param userId 用户主键
     * @return 相关XWC币种的AZ计划钱包金额
     */
    @Override
    public BigDecimal getTotalXWCAmount(String userId) {
        //从AZ钱包中获取XWC的金额
        AZPlanWallet azPlanWallet = azPlanStaticIncomeMapper.getAZPlanWallet(userId, ServerConstants.XWC);
        BigDecimal xwcAZPlanWalletBalance = BigDecimal.ZERO;
        if (null != azPlanWallet) {
            xwcAZPlanWalletBalance = azPlanWallet.getAmount();
        }
        return xwcAZPlanWalletBalance;
    }

    /**
     * 根据用户主键获取BTC兑换金额
     *
     * @param userId 用户主键
     * @return BTC兑换金额
     */
    @Override
    public BigDecimal getTotalBTCAmount(String userId) {
        AZPlanWallet azPlanWallet = azPlanStaticIncomeMapper.getAZPlanWallet(userId, ServerConstants.BTC);
        BigDecimal btcAZPlanWalletBalance = BigDecimal.ZERO;
        if (null != azPlanWallet) {
            btcAZPlanWalletBalance = azPlanWallet.getAmount();
        }
        return btcAZPlanWalletBalance;
    }

    /**
     * 根据用户主键获取兑换总金额
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return 兑换总金额
     */
    @Override
    public BigDecimal getTotalExchangeAmount(String userId, String coinType) {
        return azPlanStaticIncomeMapper.getTotalExchangeAmountForOtherCoin(userId, coinType);
    }

    /**
     * 根据用户主键获取总的资金划转金额
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return 兑换总金额
     */
    @Override
    public BigDecimal getTotalWalletAmount(String userId, String coinType) {
        return azPlanStaticIncomeMapper.getTotalWalletAmount(userId, coinType);
    }

    /**
     * 根据用户主键获取总的资金划转金额
     *
     * @param userId       用户主键
     * @param coinType     币种类型
     * @param transferType 1表示从钱包划转到AZ计划   2表示从AZ计划划转到钱包
     * @return 兑换总金额
     */
    @Override
    public BigDecimal getTotalMoneyTransferAmount(String userId, String coinType, Integer transferType) {
        return azPlanStaticIncomeMapper.getTotalMoneyTransferAmount(userId, coinType, transferType);
    }

    /**
     * 通过用户主键获取收益
     *
     * @param userId   用户主键
     * @param page     当前页码
     * @param pageSize 每页的大小
     * @return 收益流水
     */
    @Override
    public Map<String, Object> getIncomes(String userId, Integer page, Integer pageSize) throws Exception {
        Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(1 << 5);
        PageHelper.startPage(page, pageSize);
        List<IncomeBO> incomeFlows = azPlanStaticIncomeMapper.getIncomeFlows(userId);
//        for (IncomeBO incomeFlow : incomeFlows) {
//            incomeFlow.setIncomeAmount(setScale(incomeFlow.getIncomeAmount(), null));
//        }
        resultMap.put("incomeFlows", incomeFlows);
        resultMap.put("total", new PageInfo<>(incomeFlows).getTotal());
        return resultMap;
    }

    /**
     * 获取USD账单(包括收益和兑换)
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @param page     当前页码
     * @param pageSize 每页的大小
     * @return USD账单(包括收益和兑换)
     */
    @Override
    public Map<String, Object> getUsdBills(String userId, String coinType, Integer page, Integer pageSize) {
        Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(1 << 4);
        List<USDBillBO> usdBillBOList;
        USDBillBO usdBillBO;
        //如果是USD，则获取兑换和收益流水
        if (ServerConstants.USD.equalsIgnoreCase(coinType)) {
            //获取用户的兑换流水
            List<ExchangeFlow> exchangeFlows = azPlanStaticIncomeMapper.getExchanges(userId, null);
            int exchangeSize = exchangeFlows.size();
            //获取用户的收益流水
            List<IncomeFlow> incomeFlows = azPlanStaticIncomeMapper.getIncomes(userId);
            int incomeSize = incomeFlows.size();
            int totalSize = incomeSize + exchangeSize;
            usdBillBOList = Lists.newArrayListWithCapacity(totalSize);
            if (CollectionUtil.isNotEmpty(incomeFlows)) {
                for (IncomeFlow incomeFlow : incomeFlows) {
                    usdBillBO = new USDBillBO();
                    usdBillBO.setUsdAmount(setScale(incomeFlow.getIncomeAmount(), null));
                    usdBillBO.setCreateTime(incomeFlow.getCreateTime());
                    usdBillBO.setCategory(2);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(exchangeFlows)) {
                for (ExchangeFlow exchangeFlow : exchangeFlows) {
                    usdBillBO = new USDBillBO();
                    usdBillBO.setUsdAmount(setScale(exchangeFlow.getUsdAmount(), null));
                    usdBillBO.setCreateTime(exchangeFlow.getCreateTime());
                    usdBillBO.setCategory(1);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(usdBillBOList)) {
                if (null != page && null != pageSize) {
                    //按照创建时间降序排列并分页
                    List<USDBillBO> usdBillBOListPages = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).skip(pageSize * (page - 1)).limit(pageSize).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBillBOListPages);
                    resultMap.put("total", totalSize);
                } else {
                    List<USDBillBO> usdBills = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBills);
                }
            }
        }
        //如果是XWC，获取兑换流水，转出流水、转入流水、入金流水
        if (ServerConstants.XWC.equalsIgnoreCase(coinType)) {
            List<ExchangeFlow> exchangeFlows = azPlanStaticIncomeMapper.getExchanges(userId, ServerConstants.XWC);
            int exchangeSize = exchangeFlows.size();
            //获取XWC的入金流水
            List<EntryMoneyFlow> entryMoneyFlows = azPlanStaticIncomeMapper.getEntryMoneyFlows(userId, ServerConstants.XWC);
            int entryMoneyFlowSize = entryMoneyFlows.size();
            //获取转出流水、转入流水
            List<TransferInOutFlow> transferInOutFlows = azPlanStaticIncomeMapper.getTransferInOutFlowList(userId, ServerConstants.XWC, null);
            int transferInOutSize = transferInOutFlows.size();
            int totalSize = transferInOutSize + exchangeSize + entryMoneyFlowSize;
            usdBillBOList = Lists.newArrayListWithCapacity(totalSize);
            if (CollectionUtil.isNotEmpty(exchangeFlows)) {
                for (ExchangeFlow exchangeFlow : exchangeFlows) {
                    usdBillBO = new USDBillBO();
                    usdBillBO.setUsdAmount(setScale(exchangeFlow.getExchangeAmount(), null));
                    usdBillBO.setCreateTime(exchangeFlow.getCreateTime());
                    usdBillBO.setCategory(1);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(transferInOutFlows)) {
                BigDecimal xwcAmount;
                Integer flowType;
                for (TransferInOutFlow transferInOutFlow : transferInOutFlows) {
                    usdBillBO = new USDBillBO();
                    xwcAmount = transferInOutFlow.getAmount();
                    usdBillBO.setUsdAmount(setScale(xwcAmount, null));
                    usdBillBO.setCreateTime(transferInOutFlow.getCreateTime());
                    flowType = transferInOutFlow.getFlowType();
                    if (flowType == 1) {
                        usdBillBO.setCategory(4);
                    }
                    if (flowType == 2) {
                        usdBillBO.setCategory(3);
                    }
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(entryMoneyFlows)) {
                for (EntryMoneyFlow entryMoneyFlow : entryMoneyFlows) {
                    usdBillBO = new USDBillBO();
                    usdBillBO.setUsdAmount(setScale(entryMoneyFlow.getEntryAmount(), null));
                    usdBillBO.setCreateTime(entryMoneyFlow.getCreateTime());
                    usdBillBO.setCategory(5);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(usdBillBOList)) {
                if (null != page && null != pageSize) {
                    //按照创建时间降序排列并分页
                    List<USDBillBO> usdBillBOListPages = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).skip(pageSize * (page - 1)).limit(pageSize).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBillBOListPages);
                    resultMap.put("total", totalSize);
                } else {
                    List<USDBillBO> usdBills = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBills);
                }
            }
        }
        //如果是BTC，获取兑换流水，转出流水
        if (ServerConstants.BTC.equalsIgnoreCase(coinType)) {
            List<ExchangeFlow> exchangeFlows = azPlanStaticIncomeMapper.getExchanges(userId, ServerConstants.BTC);
            int exchangeSize = exchangeFlows.size();
            //获取转出流水
            List<TransferInOutFlow> transferInOutFlows = azPlanStaticIncomeMapper.getTransferInOutFlowList(userId, ServerConstants.BTC, 1);
            int transferInOutSize = transferInOutFlows.size();
            int totalSize = transferInOutSize + exchangeSize;
            usdBillBOList = Lists.newArrayListWithCapacity(totalSize);
            if (CollectionUtil.isNotEmpty(exchangeFlows)) {
                for (ExchangeFlow exchangeFlow : exchangeFlows) {
                    usdBillBO = new USDBillBO();
                    usdBillBO.setUsdAmount(setScale(exchangeFlow.getExchangeAmount(), null));
                    usdBillBO.setCreateTime(exchangeFlow.getCreateTime());
                    usdBillBO.setCategory(1);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(transferInOutFlows)) {
                BigDecimal btcAmount;
                for (TransferInOutFlow transferInOutFlow : transferInOutFlows) {
                    usdBillBO = new USDBillBO();
                    btcAmount = transferInOutFlow.getAmount();
                    usdBillBO.setUsdAmount(setScale(btcAmount, null));
                    usdBillBO.setCreateTime(transferInOutFlow.getCreateTime());
                    usdBillBO.setCategory(4);
                    usdBillBO.setUsdStatus(1);
                    usdBillBOList.add(usdBillBO);
                }
            }
            if (CollectionUtil.isNotEmpty(usdBillBOList)) {
                if (null != page && null != pageSize) {
                    //按照创建时间降序排列并分页
                    List<USDBillBO> usdBillBOListPages = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).skip(pageSize * (page - 1)).limit(pageSize).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBillBOListPages);
                    resultMap.put("total", totalSize);
                } else {
                    List<USDBillBO> usdBills = usdBillBOList.stream().sorted(Comparator.comparing(USDBillBO::getCreateTime)
                            .reversed()).collect(Collectors.toList());
                    resultMap.put("usdBills", usdBills);
                }
            }
        }
        return resultMap;
    }

    /**
     * 产生静态收益
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public void generateStaticIncome() {
        log.info("开始执行产生静态收益的定时任务");
        long begin = System.currentTimeMillis();
        List<AZPlanBalance> azPlanBalanceList = azPlanStaticIncomeMapper.getAZPlanBalances();
        List<IncomeFlow> incomeFlowList;
        List<AZPlanBalance> azPlanBalanceStatusList;
        //获取内存树
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        TreeTransaction treeTx = tree.createTransaction();
        try {
            if (null != tree) {
                Pair<List<Account>, List<AccountTree.AccountIncome>> result = tree.addNewDividendIncome(treeTx, new BigDecimal(ServerConstants.STATIC_INCOME_PERCENT));
                if (null != result) {
                    List<AccountTree.AccountIncome> accountIncomes = result.getRight();
                    if (CollectionUtil.isNotEmpty(accountIncomes)) {
                        Income income;
                        Account account;
                        Account parentAccount;
                        //直推收益（其实直推收益也属于静态收益的一部分）
                        BigDecimal directIncome;
                        //静态收益
                        BigDecimal staticIncome;
                        //动态收益
                        BigDecimal dynamicIncome;
                        //平级代数收益（一级20%, 二级10%）
                        BigDecimal generationIncome;
                        String currentKey;
                        String parentKey;
                        Integer usdtversion;
                        Integer orePoolVersion;
                        int count;
                        UserRelativeVersionBO userRelativeVersionBO;
                        BigDecimal orePool;
                        TransferInOutFlow transferInOutFlow;
                        IncomeFlow incomeFlow;
                        Integer currentUserOrePoolVersion;
                        for (AccountTree.AccountIncome accountIncome : accountIncomes) {
                            income = accountIncome.getIncome();
                            account = accountIncome.getAccount();
                            parentAccount = account.getParent();
                            parentKey = account.getParentKey();
                            if (StringUtil.isEmpty(parentKey)) {
                                parentKey = parentAccount.getKey();
                            }
                            currentKey = account.getKey();
                            directIncome = income.getDirectIncome();
                            if (null == directIncome) {
                                directIncome = BigDecimal.ZERO;
                            }
                            dynamicIncome = income.getDynamicIncome();
                            if (null == dynamicIncome) {
                                dynamicIncome = BigDecimal.ZERO;
                            }
                            generationIncome = income.getGenerationIncome();
                            if (null == generationIncome) {
                                generationIncome = BigDecimal.ZERO;
                            }
                            staticIncome = income.getStaticIncome();
                            if (null == staticIncome) {
                                staticIncome = BigDecimal.ZERO;
                            }
                            //更新有上级推荐人的账户 //fixme 系统结算是结算团队和静态收益，不会有直接推荐收益和复投收益
//                            userRelativeVersionBO = azPlanStaticIncomeMapper.getUserRelativeVersionBO(parentKey);
//                            if (directIncome.compareTo(BigDecimal.ZERO) != 0 && null != userRelativeVersionBO) {
//                                usdtversion = userRelativeVersionBO.getUsdtCoinBalanceVersion();
//                                azPlanStaticIncomeMapper.addUSDTCoinBalance(parentKey, directIncome, ServerConstants.USDT, usdtversion, new Date());
//                                orePoolVersion = userRelativeVersionBO.getOrePoolVersion();
//                                //更新推荐人的矿池大小
//                                count = azPlanStaticIncomeMapper.reduceAZPlanBalance(parentKey, directIncome, orePoolVersion, new Date());
//                                if (count > 0) {
//                                    //检查矿池大小是否为0
//                                    orePool = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(parentKey).getOrePool();
//                                    //筛选出释放完的USD，并更新状态为已经释放完，即状态改为2
//                                    if (orePool.compareTo(BigDecimal.ZERO) <= 0) {
//                                        azPlanStaticIncomeMapper.updateAZPlanStatus(parentKey, ServerConstants.RELEANSED);
//                                    }
//                                }
//                                //增加推荐人的usdt的转入流水
//                                transferInOutFlow = TransferInOutFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(parentKey)
//                                        .coinType(ServerConstants.USDT).flowType(1)
//                                        .amount(directIncome).status(ServerConstants.CONFIRMED).build();
//                                transferInOutFlow.setCreateTime(new Date());
//                                transferInOutFlow.setUpdateTime(new Date());
//                                azPlanStaticIncomeMapper.insertTransferInOutFlow(transferInOutFlow);
//                                //增加推荐人的收益流水（直推收益）
//                                incomeFlow = IncomeFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(parentKey).incomeType(2).incomeAmount(directIncome)
//                                        .build();
//                                incomeFlow.setCreateTime(new Date());
//                                incomeFlow.setUpdateTime(new Date());
//                                azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
//                            }
                            currentUserOrePoolVersion = azPlanStaticIncomeMapper.getAZPlanBalanceVersion(currentKey);
                            // TODO 更新当前用户的动态收益，是否为两个相加，待定，还有要不要矿池大小减去动态收益待定
                            dynamicIncome = BigDecimalUtils.add(dynamicIncome, String.valueOf(generationIncome));
                            if (null != currentUserOrePoolVersion) {
                                if (dynamicIncome.compareTo(BigDecimal.ZERO) != 0) {
                                    azPlanStaticIncomeMapper.updateAZPlanDynamicTotalIncome(currentKey, dynamicIncome, currentUserOrePoolVersion);
                                    //版本号+1
                                    currentUserOrePoolVersion++;
                                    //增加推荐人的收益流水（直推收益）
                                    incomeFlow = IncomeFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(currentKey).incomeType(3).incomeAmount(dynamicIncome)
                                            .build();
                                    incomeFlow.setCreateTime(new Date());
                                    incomeFlow.setUpdateTime(new Date());
                                    azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
                                }
                                //增加静态收益
                                if (staticIncome.compareTo(BigDecimal.ZERO) != 0) {
                                    count = azPlanStaticIncomeMapper.updateAZPlanStaticTotalIncome(currentKey, staticIncome, currentUserOrePoolVersion);
                                    if (count > 0) {
                                        //检查矿池大小是否为0
                                        orePool = azPlanStaticIncomeMapper.getAZPlanBalanceByUserId(currentKey).getOrePool();
                                        //筛选出释放完的USD，并更新状态为已经释放完，即状态改为2
                                        if (orePool.compareTo(BigDecimal.ZERO) <= 0) {
                                            azPlanStaticIncomeMapper.updateAZPlanStatus(currentKey, ServerConstants.RELEANSED);
                                        }
                                    }
                                    incomeFlow = new IncomeFlow();
                                    incomeFlow.setId(SnowFlakeIdUtil.defaultId());
                                    incomeFlow.setIncomeAmount(staticIncome);
                                    incomeFlow.setUserId(currentKey);
                                    incomeFlow.setIncomeType(1);
                                    incomeFlow.setCreateTime(new Date());
                                    incomeFlow.setUpdateTime(new Date());
                                    azPlanStaticIncomeMapper.insertIncomeFlow(incomeFlow);
                                }
                            }
                        }
                        //重置父节点的动态奖励
                        List<Account> resetAccounts = tree.resetCurrentRecommenderRewardArr(treeTx);
                        if (resetAccounts != null) {
                            List<String> recommendUserIds = resetAccounts.stream().map(acc -> acc.getKey()).collect(Collectors.toList());
                            //更新推荐人的状态为推荐成功，并且在本次系统结算后清空推荐人奖励，recommenderIncomeHistory
                            if (recommendUserIds != null && recommendUserIds.size() > 0) {
                                azPlanStaticIncomeMapper.resetCurrentRecommenderRewardArr(recommendUserIds);
                            }
                        }

                    }
                } else {
                    if (CollectionUtil.isNotEmpty(azPlanBalanceList)) {
                        int size = azPlanBalanceList.size();
                        BigDecimal orePool;
                        BigDecimal staticIncome;
                        int azPlanStatus;
                        incomeFlowList = Lists.newArrayListWithCapacity(size);
                        azPlanBalanceStatusList = Lists.newArrayListWithCapacity(size);
                        IncomeFlow incomeFlow;
                        for (AZPlanBalance azPlanBalance : azPlanBalanceList) {
                            orePool = azPlanBalance.getOrePool();
                            azPlanStatus = azPlanBalance.getAzPlanStatus();
                            if (azPlanStatus > 0) {
                                //筛选出释放完的USD，并更新状态为已经释放完，即状态改为2
                                if (orePool.compareTo(BigDecimal.ZERO) <= 0) {
                                    azPlanBalance.setUpdateTime(new Date());
                                    azPlanBalance.setAzPlanStatus(ServerConstants.RELEANSED);
                                    azPlanBalanceStatusList.add(azPlanBalance);
                                }
                                //状态必须达到已充值状态且矿池的大小大于0才能产生静态收益
                                if (orePool.compareTo(BigDecimal.ZERO) > 0) {
                                    //矿池产生的静态收益
                                    staticIncome = BigDecimalUtils.multiply(String.valueOf(orePool), String.valueOf("0.03")).divide(new BigDecimal("4"));
                                    azPlanBalance.setTotalStaticIncomeAmount(staticIncome);
                                    azPlanBalance.setUpdateTime(new Date());
                                    //更新静态收益
                                    azPlanStaticIncomeMapper.updateStaticIncome(azPlanBalance);
                                    //添加静态收益流水
                                    incomeFlow = new IncomeFlow();
                                    incomeFlow.setId(SnowFlakeIdUtil.defaultId());
                                    incomeFlow.setIncomeAmount(staticIncome);
                                    incomeFlow.setUserId(azPlanBalance.getUserId());
                                    incomeFlow.setIncomeType(1);
                                    incomeFlow.setCreateTime(new Date());
                                    incomeFlow.setUpdateTime(new Date());
                                    incomeFlowList.add(incomeFlow);
                                }
                            }
                        }
                        if (CollectionUtil.isNotEmpty(incomeFlowList)) {
                            //添加静态收益流水
                            azPlanStaticIncomeMapper.insertIncomeFlowList(incomeFlowList);
                        }
                        if (CollectionUtil.isNotEmpty(azPlanBalanceStatusList)) {
                            //更新矿池的状态为已释放完
                            azPlanStaticIncomeMapper.patchUpdateAZPlanStatus(azPlanBalanceStatusList);
                        }
                    }
                }
            }
            treeTx.commit();
            log.info("结束执行产生静态收益的定时任务，消耗时间：{}", System.currentTimeMillis() - begin);
        } catch (Exception e) {
            log.error("执行产生静态收益的定时任,开始执行内存树事物回滚", e);
            treeTx.rollback();
            throw e;
        }

    }

    /**
     * 定时发布公告
     */
    @Override
    public void publishNotice() {
        //查询数据库中未发布的且待上线的公告
        List<AzNotice> azNoticeList = azPlanStaticIncomeMapper.getAzNotices();
        if (CollectionUtil.isNotEmpty(azNoticeList)) {
            List<AzNotice> updateNoticeList = Lists.newArrayListWithExpectedSize(azNoticeList.size());
            String currentDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//            log.info("当前的时间：{}", currentDateTime);
            String fixedSendTime;
            for (AzNotice azNotice : azNoticeList) {
                if (null != azNotice.getFixedSendTime()) {
                    fixedSendTime = azNotice.getFixedSendTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime()
                            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//                    log.info("固定的时间：{}", fixedSendTime);
                    if (Objects.equals(currentDateTime, fixedSendTime)) {
                        azNotice.setUpTime(new Date());
                        azNotice.setUpdateTime(new Date());
                        azNotice.setPublishState(1);
                        azNotice.setUpDownState(1);
                        updateNoticeList.add(azNotice);
                    }
                }
            }
            if (CollectionUtil.isNotEmpty(updateNoticeList)) {
                azPlanStaticIncomeMapper.patchUpdateUpDownState(updateNoticeList);
            }
        }

    }

    /**
     * 获取用户AZ计划钱包的信息
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return AZ计划钱包的信息
     */
    @Override
    public AZPlanWallet getAZPlanWallet(String userId, String coinType) {
        return azPlanStaticIncomeMapper.getAZPlanWallet(userId, coinType);
    }

    /**
     * 获取某一个用户的相关币种转入与转出流水
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return 某一个用户的相关币种转入与转出流水
     */
    @Override
    public List<TransferInOutFlow> getTransferInOutFlowList(String userId, String coinType, Integer flowType) {
        return azPlanStaticIncomeMapper.getTransferInOutFlowList(userId, coinType, flowType);
    }

    /**
     * 提币
     *
     * @param withdrawOrder 提币订单PO对象
     * @return 影响的行数
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public int withdraw(WithdrawOrder withdrawOrder) {
        String coinType = withdrawOrder.getCoinType();
        //USDT的提币金额保留6位精度
        if (ServerConstants.USDT.equalsIgnoreCase(coinType)) {
            BigDecimal amount = withdrawOrder.getAmount();
            if (null != amount) {
                withdrawOrder.setAmount(amount.setScale(6, BigDecimal.ROUND_DOWN));
            }
        }
        //扣减用户的金额
        BigDecimal balance = BigDecimalUtils.add(withdrawOrder.getAmount(), String.valueOf(withdrawOrder.getFee()));
        log.info("币种类型：{}  用户的手续费：{} 用户的提币金额：{} 提币金额与手续费之和", coinType, withdrawOrder.getFee(), withdrawOrder.getAmount(), balance);
        UserCoinBalance userCoinBalance = UserCoinBalance.builder().userId(withdrawOrder.getUserId()).coinType(withdrawOrder.getCoinType())
                .version(withdrawOrder.getUserCoinBalanceVersion()).coinBalance(balance).build();
        UserCoinBalance userCoinBalanceDB = azPlanStaticIncomeMapper.getCoinBalance(userCoinBalance);
        int count = 0;
        if (null != userCoinBalanceDB) {
            count = azPlanStaticIncomeMapper.reduceUserCoinBalance(userCoinBalance);
        }
        if (count > 0) {
            //添加提币订单
            withdrawOrder.setCreateTime(new Date());
            withdrawOrder.setUpdateTime(new Date());
            withdrawOrder.setCheckState(1);
            withdrawOrder.setCheckTime(new Date());
            count += azPlanStaticIncomeMapper.insertWithdrawOrder(withdrawOrder);
        }
        return count;
    }

    /**
     * 获取提币订单
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return 提币订单
     */
    @Override
    public List<WithdrawOrder> getWithdrawOrderList(String userId, String coinType) {
        return azPlanStaticIncomeMapper.getWithdrawOrderList(userId, coinType);
    }

    /**
     * 获取推荐人的usdt版本号和矿池版本号
     *
     * @param userId 被推荐人的用户主键
     * @return 推荐人的usdt版本号和矿池版本号
     */
    @Override
    public List<UserRelativeVersionBO> getUserRelativeVersionBOList(String userId) {
        List<UserRecommender> userRecommenders = azPlanStaticIncomeMapper.getUserRecommenderByRecommenderUserId(userId);
        List<UserRelativeVersionBO> userRelativeVersionBOList = Lists.newArrayListWithExpectedSize(userRecommenders.size());
        if (CollectionUtil.isNotEmpty(userRecommenders)) {
            // 获取被推荐人的推荐人的usdt钱包版本号和被推荐人的推荐人的矿池版本号
            userRelativeVersionBOList = azPlanStaticIncomeMapper.getUserRelativeVersionBOList(userRecommenders);
        }
        return userRelativeVersionBOList;
    }

    /**
     * 获取所有用户的矿池版本号
     *
     * @return 所有用户的矿池版本号
     */
    @Override
    public List<AZPlanBalance> getUserOrePoolVersionList() {
        return azPlanStaticIncomeMapper.getUserOrePoolVersionList();
    }

    /**
     * 获取入金流水
     *
     * @return 入金流水列表
     */
    @Override
    public List<EntryMoneyFlow> getEntryMoneyFlowList() {
        return azPlanStaticIncomeMapper.getEntryMoneyFlows(null, null);
    }

    /**
     * 获取提币订单列表
     *
     * @return 提币订单列表
     */
    @Override
    public WithdrawBO getWithdrawList(WithdrawBO withdrawBO) {
        Integer page = withdrawBO.getPage();
        Integer pageSize = withdrawBO.getPageSize();
        WithdrawBO withdrawBOResult = WithdrawBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            if (page == 0) {
                page = 1;
            }
            List<WithdrawOrder> withdrawOrderList = getWithdrawOrders(withdrawBO);
            List<WithdrawOrder> WithdrawOrders;
            if (null != withdrawBO.getCheckState()) {
                WithdrawOrders = withdrawOrderList.stream().filter(item -> Objects.equals(item.getCheckState(), withdrawBO.getCheckState())).collect(Collectors.toList());
            } else {
                WithdrawOrders = withdrawOrderList;
            }
            withdrawBOResult.setTotal((long) WithdrawOrders.size());
            WithdrawOrders = WithdrawOrders.stream().sorted(Comparator.comparing(WithdrawOrder::getCreateTime)
                    .reversed()).skip(pageSize * (page - 1)).limit(pageSize).collect(Collectors.toList());
            withdrawBOResult.setWithdrawOrderList(WithdrawOrders);
        } else {
            List<WithdrawOrder> withdrawOrderList = getWithdrawOrders(withdrawBO);
            log.info("excel导出提币订单的大小：{}", withdrawOrderList.size());
            List<WithdrawOrder> WithdrawOrders = withdrawOrderList.stream().sorted(Comparator.comparing(WithdrawOrder::getCreateTime)
                    .reversed()).collect(Collectors.toList());
            withdrawBOResult.setWithdrawOrderList(WithdrawOrders);
        }
        return withdrawBOResult;
    }

    /**
     * 获取提币订单
     *
     * @return 提币订单
     */
    @Override
    public WithdrawOrder getWithdrawOrder(String id) {
        return azPlanStaticIncomeMapper.getWithdrawOrder(id);
    }

    /**
     * 更新提币的审核状态
     *
     * @param withdrawOrder 提币对象
     * @return 影响的行数
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public int updateWithdrawOrder(WithdrawOrder withdrawOrder) {
        WithdrawOrder withdrawOrderDB = azPlanStaticIncomeMapper.getWithdrawOrder(withdrawOrder.getId());
        if (null != withdrawOrderDB) {
            withdrawOrder.setUpdateTime(new Date());
            withdrawOrder.setCheckTime(new Date());
            int count = azPlanStaticIncomeMapper.updateWithdrawOrder(withdrawOrder);
            //如果提笔订单审核失败，还原用户的钱包余额
            if (count > 0 && null != withdrawOrder.getCheckState() && 3 == withdrawOrder.getCheckState()) {
                //提币金额
                BigDecimal amount = withdrawOrderDB.getAmount();
                if (null == amount) {
                    amount = BigDecimal.ZERO;
                }
                //提币手续费
                BigDecimal fee = withdrawOrderDB.getFee();
                if (null == fee) {
                    fee = BigDecimal.ZERO;
                }
                BigDecimal amountAddFee = BigDecimalUtils.add(amount, String.valueOf(fee));
                if (amountAddFee.compareTo(BigDecimal.ZERO) > 0) {
                    UserCoinBalance userCoinBalance = UserCoinBalance.builder().userId(withdrawOrder.getUserId()).coinType(withdrawOrder.getCoinType())
                            .version(withdrawOrder.getUserCoinBalanceVersion()).coinBalance(amountAddFee).build();
                    userCoinBalance.setUpdateTime(new Date());
                    count += azPlanStaticIncomeMapper.addUserCoinBalance(userCoinBalance);
                }
            }
            return count;
        }
        return 0;
    }

    /**
     * 手动打印内存树
     *
     * @return 内存树
     */
    @Override
    public String manualPrintMemoryTree(String userId) {
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        if (null != tree) {
            Account account = StringUtils.isNotEmpty(userId) ? tree.findInTreeByKey(userId) : tree.getRootNode();
            return JSON.toJSONString(account);
        }
        return null;
    }

    /**
     * 获取对应父账户下面的所有子账户累计入金金额
     */
    @Override
    public BigDecimal getAllChildAccountTotalEntryAmount(String userId) {
        //todo 这是获取直接下级推荐人信息
//        return azPlanStaticIncomeMapper.getAllChildAccountTotalEntryAmount(parentId);
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        Account account = tree.findInTreeByKey(userId);
        return account.countTotalChildrenDepositedTotal();
    }

    /**
     * 根据用户主键获取推荐人的团队用户列表
     *
     * @param userId   用户主键
     * @param page     当前页
     * @param pageSize 每页的大小
     * @return 推荐人的直推用户列表
     */
    @Override
    public Map<String, Object> getIndirectRecommends(String userId, Integer entryMoneyState, Integer page, Integer pageSize) {
        long start = System.currentTimeMillis();
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        Account account = tree.findInTreeByKey(userId);
        Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(1 << 5);
        if (null == account) {
            return resultMap;
        }
        ArrayList<String> childUserIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(account.getChildren())) {
            //获取所有的下级节点账户
            getChildUserIds(account, childUserIds);
            //移除直接下级节点
            childUserIds.removeAll(account.getChildren().stream().map(Account::getKey).collect(Collectors.toList()));
        }

        int total;
        if (!childUserIds.isEmpty()) {
            if (null != page && null != pageSize) {
                int startNum = (page - 1) * pageSize;
                PageHelper.startPage(page, pageSize);
                //获取直接推荐人列表
                List<UserRecommenderBO> userRecommends = azPlanStaticIncomeMapper.getRecommendsByUserIds(childUserIds, entryMoneyState).stream()
                        .map(recommend -> {
                            recommend.setNum(recommend.getNum() + startNum);
                            return recommend;
                        }).collect(Collectors.toList());
                resultMap.put("userRecommends", userRecommends);
                total = Integer.parseInt(String.valueOf(new PageInfo<>(userRecommends).getTotal()));
            } else {
                List<UserRecommenderBO> userRecommends = azPlanStaticIncomeMapper.getRecommendsByUserIds(childUserIds, entryMoneyState);
                resultMap.put("userRecommends", userRecommends);
                total = userRecommends.size();
            }
        } else {
            resultMap.put("userRecommends", Lists.newArrayList());
            total = 0;
        }
        resultMap.put("total", total);
        log.info("getIndirectRecommends query cost time:{}", System.currentTimeMillis() - start);
        return resultMap;
    }

    /**
     * 获取所有子级节点的账户id
     *
     * @param account
     * @param childUserIds
     */
    private void getChildUserIds(Account account, List<String> childUserIds) {
        List<String> directChildUserIds = account.getChildren().stream().map(acc -> acc.getKey()).collect(Collectors.toList());
        childUserIds.addAll(directChildUserIds);
        for (Account childAcc : account.getChildren()) {
            if (CollectionUtil.isNotEmpty(childAcc.getChildren())) {
                getChildUserIds(childAcc, childUserIds);
            }
        }
    }

    /**
     * 获取充值列表
     *
     * @param rechargeTranBO 充值记录对象
     * @return 充值列表
     */
    @Override
    public RechargeTranBO getDepositList(RechargeTranBO rechargeTranBO) {
        Integer page = rechargeTranBO.getPage();
        Integer pageSize = rechargeTranBO.getPageSize();
        RechargeTranBO rechargeTranBOResult = RechargeTranBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            List<RechargeTran> rechargeTranList = azPlanStaticIncomeMapper.getDepositList(rechargeTranBO);
            rechargeTranBOResult.setRechargeTranList(rechargeTranList);
            rechargeTranBOResult.setTotal(new PageInfo<>(rechargeTranList).getTotal());
        } else {
            List<RechargeTran> rechargeTranList = azPlanStaticIncomeMapper.getDepositList(rechargeTranBO);
            rechargeTranBOResult.setRechargeTranList(rechargeTranList);
        }
        return rechargeTranBOResult;
    }

    /**
     * 获取用户资产列表
     *
     * @param userAssetBO 用户资产业务对象
     * @return 用户资产列表
     */
    @Override
    public UserAssetBO getUserAssetList(UserAssetBO userAssetBO) {
        Integer page = userAssetBO.getPage();
        Integer pageSize = userAssetBO.getPageSize();
        UserAssetBO userAssetBOResult = UserAssetBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            List<UserAsset> userAssetList = azPlanStaticIncomeMapper.getUserAssetList(userAssetBO);
            userAssetBOResult.setUserAssetList(userAssetList);
            userAssetBOResult.setTotal(new PageInfo<>(userAssetList).getTotal());
        } else {
            List<UserAsset> userAssetList = azPlanStaticIncomeMapper.getUserAssetList(userAssetBO);
            userAssetBOResult.setUserAssetList(userAssetList);
        }
        return userAssetBOResult;
    }

    /**
     * 标记内部用户
     * 标记为内部账户后下级团队所有人均为内部账号
     *
     * @param userId 用户id
     */
    @Override
    public void markInnerUser(String userId) {
        AccountTree tree = (AccountTree) ServerConstants.treeMap.get(ServerConstants.RECOMMENG_TREE);
        Account account = tree.findInTreeByKey(userId);
        ArrayList<String> userAndChildUserIds = new ArrayList<>();
        //添加当前用户id
        userAndChildUserIds.add(userId);
        if (CollectionUtil.isNotEmpty(account.getChildren())) {
            //获取所有的下级节点账户
            getChildUserIds(account, userAndChildUserIds);
        }
        azPlanStaticIncomeMapper.updateUserType(userAndChildUserIds, User.UserTypeEnum.INNER_USER.name());
    }

    /**
     * 获取入金列表
     *
     * @param entryMoneyFlowBO 用户入金流水业务对象
     * @return 用户入金列表
     */
    @Override
    public EntryMoneyFlowBO getEntryMoneyFlowList(EntryMoneyFlowBO entryMoneyFlowBO) {
        Integer page = entryMoneyFlowBO.getPage();
        Integer pageSize = entryMoneyFlowBO.getPageSize();
        EntryMoneyFlowBO entryMoneyFlowBOResult = EntryMoneyFlowBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            List<EntryMoneyFlow> entryMoneyFlowList = azPlanStaticIncomeMapper.getEntryMoneyFlowList(entryMoneyFlowBO);
            entryMoneyFlowBOResult.setEntryMoneyFlowList(entryMoneyFlowList);
            entryMoneyFlowBOResult.setTotal(new PageInfo<>(entryMoneyFlowList).getTotal());
        } else {
            List<EntryMoneyFlow> entryMoneyFlowList = azPlanStaticIncomeMapper.getEntryMoneyFlowList(entryMoneyFlowBO);
            entryMoneyFlowBOResult.setEntryMoneyFlowList(entryMoneyFlowList);
        }
        return entryMoneyFlowBOResult;
    }

    /**
     * 获取兑换流水列表
     *
     * @param exchangeFlowBO 用户兑换流水业务对象
     * @return 用户兑换列表
     */
    @Override
    public ExchangeFlowBO getExchangeFlowList(ExchangeFlowBO exchangeFlowBO) {
        Integer page = exchangeFlowBO.getPage();
        Integer pageSize = exchangeFlowBO.getPageSize();
        ExchangeFlowBO exchangeFlowBOResult = ExchangeFlowBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            List<ExchangeFlow> exchangeFlowList = azPlanStaticIncomeMapper.getExchangeFlowList(exchangeFlowBO);
            exchangeFlowBOResult.setExchangeFlowList(exchangeFlowList);
            exchangeFlowBOResult.setTotal(new PageInfo<>(exchangeFlowList).getTotal());
        } else {
            List<ExchangeFlow> exchangeFlowList = azPlanStaticIncomeMapper.getExchangeFlowList(exchangeFlowBO);
            exchangeFlowBOResult.setExchangeFlowList(exchangeFlowList);
        }
        return exchangeFlowBOResult;
    }

    /**
     * 获取收益统计日报列表
     *
     * @param incomeStatisticsDailyBO 收益统计日报业务对象
     * @return 收益统计日报列表
     */
    @Override
    public IncomeStatisticsDailyBO getIncomeStatisticsList(IncomeStatisticsDailyBO incomeStatisticsDailyBO) {
        Integer page = incomeStatisticsDailyBO.getPage();
        Integer pageSize = incomeStatisticsDailyBO.getPageSize();
        IncomeStatisticsDailyBO incomeStatisticsDailyBOResult = IncomeStatisticsDailyBO.builder().page(page).build();
        //获取收益的日期
        List<Date> incomeDateList = azPlanStaticIncomeMapper.getIncomeDateList(incomeStatisticsDailyBO);
        if (null != page && null != pageSize) {
            List<IncomeStatisticsBO> incomeStatisticsList = getIncomeStatisticsList(incomeDateList, incomeStatisticsDailyBO);
            if (CollectionUtil.isNotEmpty(incomeStatisticsList)) {
                if (page == 0) {
                    page = 1;
                }
                incomeStatisticsDailyBOResult.setIncomeStatisticsList(incomeStatisticsList.stream().skip(pageSize * (page - 1)).limit(pageSize).collect(Collectors.toList()));
                incomeStatisticsDailyBOResult.setTotal((long) incomeDateList.size());
            }
        } else {
            List<IncomeStatisticsBO> incomeStatisticsList = getIncomeStatisticsList(incomeDateList, incomeStatisticsDailyBO);
            if (CollectionUtil.isNotEmpty(incomeStatisticsList)) {
                incomeStatisticsDailyBOResult.setIncomeStatisticsList(incomeStatisticsList);
            }
        }
        return incomeStatisticsDailyBOResult;
    }

    /**
     * 获取收益明细列表
     *
     * @param incomeStatisticsDailyBO 收益统计日报业务对象
     * @return 收益明细列表
     */
    @Override
    public IncomeStatisticsDailyBO getIncomeDetailList(IncomeStatisticsDailyBO incomeStatisticsDailyBO) {
        Integer page = incomeStatisticsDailyBO.getPage();
        Integer pageSize = incomeStatisticsDailyBO.getPageSize();
        IncomeStatisticsDailyBO incomeStatisticsDailyBOResult = IncomeStatisticsDailyBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            List<IncomeFlow> incomeFlowsList = azPlanStaticIncomeMapper.getIncomeDetailList(incomeStatisticsDailyBO);
            incomeStatisticsDailyBOResult.setIncomeList(incomeFlowsList);
            incomeStatisticsDailyBOResult.setTotal(new PageInfo<>(incomeFlowsList).getTotal());
        } else {
            List<IncomeFlow> incomeFlowsList = azPlanStaticIncomeMapper.getIncomeDetailList(incomeStatisticsDailyBO);
            incomeStatisticsDailyBOResult.setIncomeList(incomeFlowsList);
        }
        return incomeStatisticsDailyBOResult;
    }

    /**
     * 站内转账
     *
     * @param inSiteUserBO 站内转账业务对象
     * @return 影响的行数
     */
    @Override
    @Transactional(transactionManager = ServerConstants.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public int inSiteTransfer(InSiteUserBO inSiteUserBO) {
        //扣减转出账户的余额
        BigDecimal feeAmount = BigDecimalUtils.add(inSiteUserBO.getTransferAmount(), String.valueOf(inSiteUserBO.getFee()));
        UserCoinBalance transferOutUserCoinBalance = UserCoinBalance.builder().coinBalance(feeAmount)
                .coinType(inSiteUserBO.getCoinType()).userId(inSiteUserBO.getTransferOutUserId()).build();
        //增加转入账户的余额
        UserCoinBalance transferInUserCoinBalance = UserCoinBalance.builder().coinType(inSiteUserBO.getCoinType())
                .coinBalance(inSiteUserBO.getTransferAmount()).userId(inSiteUserBO.getTransferInUserId()).build();
        String coinType = inSiteUserBO.getCoinType();
        if (ServerConstants.XWC.equals(coinType)) {
            transferOutUserCoinBalance.setVersion(inSiteUserBO.getTransferOutXwcCoinBalanceVersion());
            transferInUserCoinBalance.setVersion(inSiteUserBO.getTransferInXwcCoinBalanceVersion());
        }
        if (ServerConstants.BTC.equals(coinType)) {
            transferOutUserCoinBalance.setVersion(inSiteUserBO.getTransferOutBtcCoinBalanceVersion());
            transferInUserCoinBalance.setVersion(inSiteUserBO.getTransferInBtcCoinBalanceVersion());
        }
        if (ServerConstants.USDT.equals(coinType)) {
            transferOutUserCoinBalance.setVersion(inSiteUserBO.getTransferOutUsdtCoinBalanceVersion());
            transferInUserCoinBalance.setVersion(inSiteUserBO.getTransferInUsdtCoinBalanceVersion());
        }
        transferOutUserCoinBalance.setUpdateTime(new Date());
        transferInUserCoinBalance.setUpdateTime(new Date());
        int inCount = azPlanStaticIncomeMapper.addUserCoinBalance(transferInUserCoinBalance);
        //这样做方便查询转入方与转出方的对应关系
        Date date = new Date();
        if (inCount > 0) {
            TransferInOutFlow transferInOutFlow = TransferInOutFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(inSiteUserBO.getTransferInUserId())
                    .coinType(coinType).flowType(3).fee(BigDecimal.ZERO).amount(inSiteUserBO.getTransferAmount()).status(ServerConstants.CONFIRMED).build();
            transferInOutFlow.setCreateTime(date);
            transferInOutFlow.setUpdateTime(date);
            inCount += azPlanStaticIncomeMapper.insertTransferInOutFlow(transferInOutFlow);
            //获取转账发起人的手机号
            String userPhone = azPlanStaticIncomeMapper.getUserPhoneByUserId(inSiteUserBO.getTransferOutUserId());
            //插入站内转入的记录
            RechargeTran rechargeTran = RechargeTran.builder().id(SnowFlakeIdUtil.defaultId()).userId(inSiteUserBO.getTransferInUserId())
                    .coinType(inSiteUserBO.getCoinType()).coinAddress(userPhone).coinAmount(inSiteUserBO.getTransferAmount())
                    .txId(UUID.randomUUID().toString()).confirmations(0).txTime(new Date()).rechargeType(3).createTime(new Date()).build();
            inCount += azPlanStaticIncomeMapper.insertRechargeTran(rechargeTran);
        }
        int outCount = azPlanStaticIncomeMapper.reduceUserCoinBalance(transferOutUserCoinBalance);
        if (outCount > 0) {
            TransferInOutFlow transferInOutFlow = TransferInOutFlow.builder().id(SnowFlakeIdUtil.defaultId()).userId(inSiteUserBO.getTransferOutUserId())
                    .coinType(coinType).flowType(4).fee(inSiteUserBO.getFee()).amount(inSiteUserBO.getTransferAmount()).status(ServerConstants.CONFIRMED).build();
            transferInOutFlow.setCreateTime(date);
            transferInOutFlow.setUpdateTime(date);
            outCount += azPlanStaticIncomeMapper.insertTransferInOutFlow(transferInOutFlow);
        }
        return inCount + outCount;
    }

    /**
     * 添加公告
     *
     * @param azNotice 公告对象
     * @return 影响的行数
     */
    @Override
    public int addAzNotice(AzNotice azNotice) {
        if (null != azNotice) {
            azNotice.setId(SnowFlakeIdUtil.defaultId());
            azNotice.setCreateTime(new Date());
            azNotice.setUpdateTime(new Date());
            if (StringUtil.isEmpty(azNotice.getNoticeContent())) {
                azNotice.setIsHaveText(0);
            } else {
                azNotice.setIsHaveText(1);
            }
            if (StringUtil.isEmpty(azNotice.getEnglishNoticeContent())) {
                azNotice.setIsHaveEnText(0);
            } else {
                azNotice.setIsHaveEnText(1);
            }
            if (StringUtil.isEmpty(azNotice.getKrNoticeContent())) {
                azNotice.setIsHaveKrText(0);
            } else {
                azNotice.setIsHaveKrText(1);
            }
            if (StringUtil.isEmpty(azNotice.getJpNoticeContent())) {
                azNotice.setIsHaveJpText(0);
            } else {
                azNotice.setIsHaveJpText(1);
            }
            if (StringUtil.isEmpty(azNotice.getRaNoticeContent())) {
                azNotice.setIsHaveRaText(0);
            } else {
                azNotice.setIsHaveRaText(1);
            }
            if (StringUtil.isEmpty(azNotice.getHkNoticeContent())) {
                azNotice.setIsHaveHkText(0);
            } else {
                azNotice.setIsHaveHkText(1);
            }
            if (null != azNotice.getFixedSendTime()) {
                azNotice.setPublishState(0);
                azNotice.setUpDownState(2);
            } else {
                azNotice.setPublishState(1);
                azNotice.setUpDownState(1);
                azNotice.setUpTime(new Date());
            }
            return azPlanStaticIncomeMapper.addAzNotice(azNotice);
        }
        return 0;
    }

    /**
     * 修改公告
     *
     * @param azNotice 公告对象
     * @return 影响的行数
     */
    @Override
    public int updateAzNotice(AzNotice azNotice) {
        AzNotice azNoticeDB = azPlanStaticIncomeMapper.getAzNoticeById(azNotice.getId());
        if (null != azNoticeDB) {
            azNotice.setUpdateTime(new Date());
            if (StringUtil.isNotEmpty(azNotice.getNoticeContent())) {
                azNotice.setIsHaveText(1);
            }
            if (StringUtil.isNotEmpty(azNotice.getEnglishNoticeContent())) {
                azNotice.setIsHaveEnText(1);
            }
            if (StringUtil.isNotEmpty(azNotice.getKrNoticeContent())) {
                azNotice.setIsHaveKrText(1);
            }
            if (StringUtil.isNotEmpty(azNotice.getJpNoticeContent())) {
                azNotice.setIsHaveJpText(1);
            }
            if (StringUtil.isNotEmpty(azNotice.getRaNoticeContent())) {
                azNotice.setIsHaveRaText(1);
            }
            if (StringUtil.isNotEmpty(azNotice.getHkNoticeContent())) {
                azNotice.setIsHaveHkText(1);
            }
            if (null != azNotice.getFixedSendTime()) {
                azNotice.setPublishState(0);
                azNotice.setUpDownState(2);
            } else {
                azNotice.setPublishState(1);
                azNotice.setUpDownState(1);
                azNotice.setUpTime(new Date());
            }
            return azPlanStaticIncomeMapper.updateAzNotice(azNotice);
        }
        return 0;
    }

    /**
     * 根据公告主键查询单个公告
     *
     * @param id 主键
     * @return 公告对象
     */
    @Override
    public AzNotice getAzNoticeById(String id) {
        return azPlanStaticIncomeMapper.getAzNoticeById(id);
    }

    /**
     * 根据相关的检索条件获取公告列表
     *
     * @param azNoticeBO 公告业务对象
     * @return 公告业务对象
     */
    @Override
    public AzNoticeBO getAzNoticeList(AzNoticeBO azNoticeBO) {
        Integer page = azNoticeBO.getPage();
        Integer pageSize = azNoticeBO.getPageSize();
        AzNoticeBO azNoticeBOResult = AzNoticeBO.builder().page(page).build();
        if (null != page && null != pageSize) {
            PageHelper.startPage(page, pageSize);
            String callSource = azNoticeBO.getCallSource();
            List<AzNotice> azNoticeList = Lists.newArrayList();
            if (CallSourceEnum.ANDROID.name().equals(callSource) || CallSourceEnum.IOS.name().equals(callSource)) {
                String language = azNoticeBO.getLanguage();
                if (ServerConstants.CN.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.ES.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullEnglishNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.JP.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullJpNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.HK.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullHkNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.KR.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullKrNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.RA.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullRaNoticeTitle(azNoticeBO);
                }
            } else {
                azNoticeList = azPlanStaticIncomeMapper.getAzNoticeList(azNoticeBO);
            }
            azNoticeBOResult.setAzNoticeList(azNoticeList);
            azNoticeBOResult.setTotal(new PageInfo<>(azNoticeList).getTotal());
        } else {
            String callSource = azNoticeBO.getCallSource();
            List<AzNotice> azNoticeList = Lists.newArrayList();
            if (CallSourceEnum.ANDROID.name().equals(callSource) || CallSourceEnum.IOS.name().equals(callSource)) {
                String language = azNoticeBO.getLanguage();
                if (ServerConstants.CN.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.ES.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullEnglishNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.JP.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullJpNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.HK.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullHkNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.KR.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullKrNoticeTitle(azNoticeBO);
                }
                if (ServerConstants.RA.equals(language)) {
                    azNoticeList = azPlanStaticIncomeMapper.getAzNoticeListByNotNullRaNoticeTitle(azNoticeBO);
                }
            } else {
                azNoticeList = azPlanStaticIncomeMapper.getAzNoticeList(azNoticeBO);
            }
            azNoticeBOResult.setAzNoticeList(azNoticeList);
        }
        return azNoticeBOResult;
    }

    /**
     * 修改公告的上线与下线状态
     *
     * @param azNotice 公告对象
     * @return 影响的行数
     */
    @Override
    public int upDownAzNotice(AzNotice azNotice) {
        AzNotice azNoticeDB = azPlanStaticIncomeMapper.getAzNoticeById(azNotice.getId());
        if (null != azNoticeDB) {
            azNotice.setUpdateTime(new Date());
            if (1 == azNotice.getUpDownState()) {
                azNotice.setUpTime(new Date());
            }
            return azPlanStaticIncomeMapper.upDownAzNotice(azNotice);
        }
        return 0;
    }

    /**
     * 根据用户主键和币种类型获取未审核的（冻结的）的提币总金额
     *
     * @param userId   用户主键
     * @param coinType 币种类型
     * @return 冻结的金额
     */
    @Override
    public BigDecimal getFrozenAmount(String userId, String coinType) {
        //获取总的手续费
        BigDecimal totalFee = azPlanStaticIncomeMapper.getFrozenFee(userId, coinType);
        if (null == totalFee) {
            totalFee = BigDecimal.ZERO;
        }
        BigDecimal withdrawAmount = azPlanStaticIncomeMapper.getFrozenAmount(userId, coinType);
        if (null == withdrawAmount) {
            withdrawAmount = BigDecimal.ZERO;
        }
        BigDecimal totalFrozenAmount = BigDecimalUtils.add(withdrawAmount, String.valueOf(totalFee));
        return totalFrozenAmount;
    }

    /**
     * 保留小数位数
     *
     * @param amount 金额
     * @param scale  保留位数
     * @return 保留精度的小数
     */
    private String setScale(BigDecimal amount, Integer scale) {
        if (null != scale) {
            return BigDecimalUtils.keepDecimalPlaces(amount, scale).toPlainString();
        } else {
            if (null == amount) {
                amount = BigDecimal.ZERO;
            }
            return amount.toPlainString();
        }
    }

    /**
     * 重新加载内存树
     */
    public void reloadAccountTree() {
        try {
            log.info("初始化推荐树开始");
            //执行初始化操作，将用户的推荐关系加载到内存中
            //整个树在内存中维护一份，重启时从数据库account表恢复树。也可以直接把account保存到数据库中
            List<UserRecommender> userRecommenderList = azPlanStaticIncomeMapper.getUserRecommenderByRecommenderList(null);
            List<UserRecommender> notRecommendUserList = azPlanStaticIncomeMapper.getNotRecommendUserList();
            int userRecommenderSize = userRecommenderList.size();
            int notRecommendUserSize = notRecommendUserList.size();
            List<UserRecommender> notAddedToMapUsers = Lists.newArrayListWithCapacity(userRecommenderSize + notRecommendUserSize);
            notAddedToMapUsers.addAll(userRecommenderList);
            notAddedToMapUsers.addAll(notRecommendUserList);
            if (CollectionUtil.isNotEmpty(notAddedToMapUsers)) {
                //被推荐人的主键
                String recommendUserId;
                //被推荐人的手机号
                String recommendUserPhone;
                //被推荐人的矿池大小
                Integer recommendAmount;
                //当前被推荐人的累计入金
                BigDecimal totalEntryAmount;
                //矿池大小
                BigDecimal orePool;
                //当前被推荐人的静态收益
                String userVipLevel;
                Integer level = 0;
                String recommendId;

                List<Account> allNodes = Lists.newArrayListWithCapacity(userRecommenderSize + notRecommendUserSize);
                Account node;
                for (UserRecommender userRecommender : notAddedToMapUsers) {
                    recommendUserId = userRecommender.getRecommenderUserId();
                    recommendUserPhone = userRecommender.getRecommenderUserPhone();
                    if (StringUtil.isNotEmpty(recommendUserId)) {
                        node = new Account();
                        node.setKey(recommendUserId);
                        node.setPhone(recommendUserPhone);
                        recommendId = userRecommender.getRecommenderId();
                        if (StringUtil.isNotEmpty(recommendId)) {
                            node.setParentKey(recommendId);
                        }
                        // set level, 入金金额, childs count等
                        userVipLevel = userRecommender.getUserVipLevel();
                        if (ServerConstants.ONE_LEVEL.equals(userVipLevel)) {
                            level = 1;
                        }
                        if (ServerConstants.TWO_LEVEL.equals(userVipLevel)) {
                            level = 2;
                        }
                        if (ServerConstants.THREE_LEVEL.equals(userVipLevel)) {
                            level = 3;
                        }
                        node.setLevel(level);
                        totalEntryAmount = userRecommender.getTotalEntryAmount();
                        if (null == totalEntryAmount) {
                            totalEntryAmount = BigDecimal.ZERO;
                        }
                        node.setDepositedTotal(totalEntryAmount);
                        orePool = userRecommender.getOrePool();
                        if (null == orePool) {
                            orePool = BigDecimal.ZERO;
                        }
                        node.setDividendPool(orePool);
                        recommendAmount = userRecommender.getRecommendPersonAmount();
                        if (null == recommendAmount) {
                            recommendAmount = 0;
                        }
                        node.setNewChildCountInCurrentDeposit(recommendAmount);
                        if (null != userRecommender.getRecommenderIncomeHistory()) {
                            List<BigDecimal> recommenderRewards = Arrays.stream(userRecommender.getRecommenderIncomeHistory().split(",")).map(BigDecimal::new).collect(Collectors.toList());
                            node.setCurrentRecommenderRewardArr(recommenderRewards.toArray(new BigDecimal[recommenderRewards.size()]));
                        }
                        allNodes.add(node);
                    }
                }
                AccountTree tree = AccountTree.create(allNodes);

                tree.autoSetAllAccountsLevels();

                //树构造完成以后放入map中
                ServerConstants.treeMap.put(ServerConstants.RECOMMENG_TREE, tree);
                log.info("初始化推荐树已完成。。。");
            }
        } catch (Exception e) {
            log.error("初始化推荐树报错", e);
        }
    }

    /**
     * 获取提币订单的站内转账和提币订单列表
     *
     * @param withdrawBO 提币订单业务对象
     * @return 提币订单列表
     */
    private List<WithdrawOrder> getWithdrawOrders(WithdrawBO withdrawBO) {
        List<WithdrawOrder> withdrawOrderList = azPlanStaticIncomeMapper.getWithdrawList(withdrawBO);
        //获取站内转入的站内转出流水
        List<TransferInOutFlow> transferInOutFlowList = azPlanStaticIncomeMapper.getInSiteTransferInOutFlowList(withdrawBO);
        if (CollectionUtil.isNotEmpty(transferInOutFlowList)) {
            List<WithdrawOrder> transferInOutFlows = Lists.newArrayListWithExpectedSize(transferInOutFlowList.size());
            WithdrawOrder withdrawOrder;
            for (TransferInOutFlow transferInOutFlow : transferInOutFlowList) {
                withdrawOrder = new WithdrawOrder();
                withdrawOrder.setUserId(transferInOutFlow.getUserId());
                withdrawOrder.setUserAccount(transferInOutFlow.getUserAccount());
                withdrawOrder.setCoinType(transferInOutFlow.getCoinType());
                withdrawOrder.setCreateTime(transferInOutFlow.getCreateTime());
                withdrawOrder.setAmount(transferInOutFlow.getAmount());
                withdrawOrder.setFee(transferInOutFlow.getFee());
                withdrawOrder.setToAddr(transferInOutFlow.getTransferInUserPhone());
                withdrawOrder.setCheckState(null);
                withdrawOrder.setCheckTime(null);
                transferInOutFlows.add(withdrawOrder);
            }
            withdrawOrderList.addAll(transferInOutFlows);
        }
        return withdrawOrderList;
    }

    /**
     * 获取收益日报统计列表
     *
     * @param incomeDateList          收益日期列表
     * @param incomeStatisticsDailyBO 收益统计日报业务对象
     * @return 收益统计日报业务对象列表
     */
    private List<IncomeStatisticsBO> getIncomeStatisticsList(List<Date> incomeDateList, IncomeStatisticsDailyBO incomeStatisticsDailyBO) {
        List<IncomeFlow> incomeFlowList = azPlanStaticIncomeMapper.getIncomeFlowList(incomeStatisticsDailyBO);
        List<IncomeStatisticsBO> incomeStatisticsList = Lists.newArrayListWithExpectedSize(incomeDateList.size());
        if (CollectionUtil.isNotEmpty(incomeDateList) && CollectionUtil.isNotEmpty(incomeFlowList)) {
            LocalDate localDate;
            IncomeStatisticsBO incomeStatisticsBO;
            BigDecimal totalMinerReward;
            BigDecimal totalInviteReward;
            BigDecimal totalSystemReward;
            for (Date date : incomeDateList) {
                totalMinerReward = BigDecimal.ZERO;
                totalInviteReward = BigDecimal.ZERO;
                totalSystemReward = BigDecimal.ZERO;
                localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                for (IncomeFlow incomeFlow : incomeFlowList) {
                    if (localDate.equals(incomeFlow.getCreateTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate())) {
                        if (1 == incomeFlow.getIncomeType()) {
                            totalMinerReward = BigDecimalUtils.add(totalMinerReward, String.valueOf(incomeFlow.getIncomeAmount()));
                        }
                        if (2 == incomeFlow.getIncomeType()) {
                            totalInviteReward = BigDecimalUtils.add(totalInviteReward, String.valueOf(incomeFlow.getIncomeAmount()));
                        }
                        if (3 == incomeFlow.getIncomeType()) {
                            totalSystemReward = BigDecimalUtils.add(totalSystemReward, String.valueOf(incomeFlow.getIncomeAmount()));
                        }
                    }

                }
                incomeStatisticsBO = new IncomeStatisticsBO();
                incomeStatisticsBO.setIncomeDate(date);
                incomeStatisticsBO.setPosMinerReward(totalMinerReward);
                incomeStatisticsBO.setInviteReward(totalInviteReward);
                incomeStatisticsBO.setSystemReward(totalSystemReward);
                incomeStatisticsList.add(incomeStatisticsBO);
            }
        }
        return incomeStatisticsList;
    }
}
